package com.github.ylt.protocol.http.handler;

import com.github.ylt.annotations.Action;
import com.github.ylt.annotations.ActionParam;
import com.github.ylt.annotations.SOAPService;
import com.github.ylt.conf.Configure;
import com.github.ylt.protocol.event.HttpEvent;
import com.github.ylt.protocol.http.Headers;
import com.github.ylt.protocol.http.server.exchange.HttpExchange;
import com.github.ylt.protocol.http.server.exchange.HttpExchangeImpl;
import com.github.ylt.utils.OSUtils;
import com.github.ylt.utils.XmlUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.DocumentException;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class HttpHandlerImpl implements HttpHandler {
    private final Log LOG = LogFactory.getLog( HttpHandlerImpl.class );
    private final String PACKAGENAME = Configure.DEFAULT_SCAN_PACKAGE;
    private final Map<String, List<Triple<String,Class<?>,Method>>> tripleMap = new ConcurrentHashMap<>();

    @Override
    public void handler(HttpExchange exchange) {
        try {
            cacheClass();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        String action =null;
        Headers headers = exchange.getRequestHeaders();
        InputStream requestBody = exchange.getRequestBody();
        Map<String, Object> stringObjectMap = null;
        try {
            if(requestBody.available()>0){
                stringObjectMap = parseRequestParameter(headers,requestBody);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if(exchange.isSOAPRequest()){
            action = exchange.getAction();
        }
        if(action==null){
            if(exchange.isGetRequest() && "/".equals(exchange.getRequestURL().getPath())){
                exchange.getResponseHeaders().add("Content-Type", "text/html;charset=\"utf-8\"");
                String html="<html><head></head><body>hello world</body></html>";
                try {
                    exchange.sendResponse(html);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            //请求的为静态文件直接返回
            if(exchange.isGetRequest() && exchange.getRequestURL().getPath().equals("/description.xml")){
                URL resource = HttpHandlerImpl.class.getResource("/xml/description.xml");
                String path = resource.getPath();
                File file = new File(path);
                byte[] bytes = new byte[(int) file.length()];
                try(RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r")){
                    randomAccessFile.read(bytes,0,bytes.length);
                    exchange.sendResponse(new String(bytes));
                } catch (
                        FileNotFoundException e) {
                    try {
                        exchange.setResponseCode(404);
                        exchange.getResponseHeaders().add("Content-Type", "text/html;charset=\"utf-8\"");
                        exchange.sendResponse("未找到该资源");
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return;
            }
            if(exchange.isGetRequest() && exchange.getRequestURL().getPath().equals("/dlna/AVTransport.xml")){
                URL resource = HttpHandlerImpl.class.getResource("/xml/AVTransport.xml");
                String path = resource.getPath();
                File file = new File(path);
                byte[] bytes = new byte[(int) file.length()];
                try(RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r")){
                    randomAccessFile.read(bytes,0,bytes.length);
                    exchange.sendResponse(new String(bytes));
                } catch (
                        FileNotFoundException e) {
                    try {
                        exchange.setResponseCode(404);
                        exchange.getResponseHeaders().add("Content-Type", "text/html;charset=\"utf-8\"");
                        exchange.sendResponse("未找到该资源");
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return;
            }
            if(exchange.isGetRequest() && exchange.getRequestURL().getPath().equals("/dlna/RenderingControl.xml")){
                URL resource = HttpHandlerImpl.class.getResource("/xml/RenderingControl.xml");
                String path = resource.getPath();
                File file = new File(path);
                byte[] bytes = new byte[(int) file.length()];
                try(RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r")){
                    randomAccessFile.read(bytes,0,bytes.length);
                    exchange.sendResponse(new String(bytes));
                } catch (
                        FileNotFoundException e) {
                    try {
                        exchange.setResponseCode(404);
                        exchange.getResponseHeaders().add("Content-Type", "text/html;charset=\"utf-8\"");
                        exchange.sendResponse("未找到该资源");
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return;
            }
            if(exchange.isGetRequest() && exchange.getRequestURL().getPath().equals("/dlna/ConnectionManager.xml")){
                URL resource = HttpHandlerImpl.class.getResource("/xml/ConnectionManager.xml");
                String path = resource.getPath();
                File file = new File(path);
                byte[] bytes = new byte[(int) file.length()];
                try(RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r")){
                    randomAccessFile.read(bytes,0,bytes.length);
                    exchange.sendResponse(new String(bytes));
                } catch (
                        FileNotFoundException e) {
                    try {
                        exchange.setResponseCode(404);
                        exchange.getResponseHeaders().add("Content-Type", "text/html;charset=\"utf-8\"");
                        exchange.sendResponse("未找到该资源");
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return;
            }
        }else{
            //soap请求
            exchange.getResponseHeaders().add("Server", OSUtils.getInstance().getOsName()+"/"+OSUtils.getInstance().getOsVersion()+" "+"upnp/1.0 我是你爹/1.0");
            exchange.getResponseHeaders().add("Allow","GET, HEAD, POST, SUBSCRIBE, UNSUBSCRIBE");
            exchange.getResponseHeaders().add("Ext","");

            URI requestURL = exchange.getRequestURL();
            Object result = "";
            List<Triple<String, Class<?>, Method>> triples = this.tripleMap.get(requestURL.getPath());
            for(Triple<String, Class<?>, Method> triple : triples){
                if(triple.getLeft().equals(exchange.getAction().replace("\"",""))){
                    try {
                        Constructor<?> declaredConstructor = triple.getMiddle().getDeclaredConstructor();
                        Object o = declaredConstructor.newInstance();
                        Parameter[] parameters = triple.getRight().getParameters();
                        //设置参数
                        List<String> parameterList = new ArrayList<>();

                        if(parameters != null && parameters.length > 0){
                            for(Parameter parameter : parameters){
                                String value = parameter.getAnnotation(ActionParam.class).value();
                                parameterList.add(value);
                            }
                            Object[] objects = new Object[parameterList.size()];
                            for(int i = 0; i < parameterList.size(); i++){
                                if(stringObjectMap != null){
                                    objects[i] = stringObjectMap.get(parameterList.get(i));
                                }

                            }
                            result = triple.getRight().invoke(o,objects);
                        }
                        break;
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            if(result instanceof String r){
                try {
                    exchange.sendResponse(r);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

        }


    }

    private void cacheClass() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String packageName = "com.github.ylt.controller";
        List<Class<?>> classes = findClassesInPackage(packageName);
        List<Triple<String,Class<?>,Method>> list;
        for (Class<?> clazz : classes) {
            list = new ArrayList<>();
            boolean isSOAPService = clazz.isAnnotationPresent(SOAPService.class);
            String value;
            if(isSOAPService){
                value = clazz.getAnnotation(SOAPService.class).value();
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    boolean isAction = method.isAnnotationPresent(Action.class);
                    if(isAction){
                        Action annotation = method.getAnnotation(Action.class);
                        String namespace = annotation.namespace();
                        list.add(Triple.of(namespace,clazz,method));
                    }
                }
                this.tripleMap.put(value,list);
            }
        }
    }

    private List<Class<?>> findClassesInPackage(String packageName) throws IOException, ClassNotFoundException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        List<Class<?>> classes = new ArrayList<>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes;
    }

    private List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class<?>> classes = new ArrayList<>();
        if (!directory.exists()) return classes;

        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    classes.addAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                    classes.add(Class.forName(className));
                }
            }
        }
        return classes;
    }
    public Map<String, Object> parseRequestParameter(Headers headers,InputStream requestBody) {
        String first = headers.get("Content-Length").getFirst();
        byte[] bytes = new byte[Integer.parseInt(first)];
        try {
            requestBody.read(bytes,0,bytes.length);
            Map<String, Object> parse = XmlUtils.getInstance().parse(new String(bytes, StandardCharsets.UTF_8));
            return parse;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }
}
